package maple.core.gateways;

import java.util.logging.Level;

import jade.domain.DFService;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAException;

import com.phidgets.InterfaceKitPhidget;
import com.phidgets.PhidgetException;

import maple.core.Gateway;
import maple.lang.Command;
import maple.lang.Language;
import maple.util.Profile;

public class PhidgetSBC extends Gateway
{
	private enum Port{ A, DI, DO };
	
	private final int CONNECTION_PORT = 5001;
	private final int WAIT_TIME = 10000;
	
	private InterfaceKitPhidget _handler;

	public PhidgetSBC()
	{
		super( 4 );
		
		_handler = null;
	}

	protected void setup()
	{
		super.setup();

		Command.Message message = Command.Message.ERROR;
		try
		{
			_handler = new InterfaceKitPhidget();
			_handler.openAny( _arguments[ 1 ], CONNECTION_PORT, _arguments[ 2 ] );
			_handler.waitForAttachment( WAIT_TIME );
			if( _handler.isAttachedToServer() )
			{
				_handler.setRatiometric( Boolean.valueOf( _arguments[ 3 ] ) );

				if( load() == Command.Message.OK )
				{
					message = modify();
				}
			}
		}
		catch( PhidgetException e )
		{
			_logger.log( Level.WARNING, "", e );
			message = Command.Message.EXCEPTION_PHIDGET;
		}

		if( message != Command.Message.OK )
		{
			doDelete();
		}
	}

	protected void takeDown()
	{
		super.takeDown();
		
		try
		{
			if( _handler != null )
			{
				_handler.close();
			}
		}
		catch( PhidgetException e )
		{
			_logger.log( Level.WARNING, "", e );
		}
	}

	/**
	 * return true if adding the specified port with the specified sensitivity successful
	 *
	 * @param   port		the specified port
	 * @param   sensitivity the specified sensitivity
	 * @return  true if adding the specified port with the specified sensitivity successful
	 *
	 * @author  snowfoxy
	 * @since   2011-11-22
	 */
	protected boolean insertPort( final String port, final String sensitivity )
	{
		try
		{
			String[] tokens = splitPort( port );
			if( tokens != null )
			{
				int index = Integer.parseInt( tokens[ 1 ] );
				
				switch( Port.valueOf( tokens[ 0 ] ) )
				{
					case A:
						if( index < _handler.getSensorCount() )
						{
							_handler.setSensorChangeTrigger( index, Integer.parseInt( sensitivity ) );
							return true;
						}
						break;
					case DI:
						if( index < _handler.getInputCount() )
						{
							_handler.setDataRate( index, Integer.parseInt( sensitivity ) );
							return true;
						}
						break;
					case DO:
						if( index < _handler.getOutputCount() )
						{
							_handler.setOutputState( index, false );
							return true;
						}
						break;
				}
			}
		}
		catch( PhidgetException e )
		{
			_logger.log( Level.WARNING, "", e );
		}
		return false;
	}

	/**
	 * return the status of modifying the services
	 *
	 * @return	the status of modifying the services
	 *
	 * @author	snowfoxy
	 * @since	2011-11-22
	 */
	protected Command.Message modify()
	{
		try
		{
			Command.Message message = save();
			if( message == Command.Message.OK )
			{
				DFAgentDescription agent = getInstance( _names, _types, null, null, null );
				for( String port : _sensors.keySet() )
				{
					Profile profile = _sensors.get( port );

					String[] tokens = splitPort( port );
					switch( Port.valueOf( tokens[ 0 ] ) )
					{
						case A: case DI:
							agent.addServices( profile.toService( getName(), Language.Instruction.READ, port ) );
							break;
						case DO:
							agent.addServices( profile.toService( getName(), Language.Instruction.WRITE, port ) );
							break;
					}
				}
				DFService.modify( this, agent );
				return Command.Message.OK;
			}
			return message;
		}
		catch( FIPAException e )
		{
			_logger.log( Level.WARNING, "", e );
			return Command.Message.EXCEPTION_FIPA;
		}
	}

	/**
	 * return the value of the specified port
	 *
	 * @param	port	the specified port
	 * @return	the value of the specified port
	 *
	 * @author	snowfoxy
	 * @since	2011-11-04
	 */
	protected double read( final String port )
	{
		double result = -1;
		try
		{
			String[] tokens = splitPort( port );
			switch( Port.valueOf( tokens[ 0 ] ) )
			{
				case A:
					result = _handler.getSensorValue( Integer.parseInt( tokens[ 1 ] ) );
					break;
				case DI:
					result = 0;
					if( _handler.getInputState( Integer.parseInt( tokens[ 1 ] ) ) )
					{
						result = 1;
					}
					break;
			}
		}
		catch( PhidgetException e )
		{
			_logger.log( Level.WARNING, "", e );
		}
		return result;
	}

	/**
	 * return the type and the index of the specified port
	 *
	 * @param   port    the specified port
	 * @return  the type and the index of the specified port
	 *
	 * @author  snowfoxy
	 * @since   2011-11-15
	 */
	private String[] splitPort( final String port )
	{
		String[] tokens = port.split( Gateway.DELIMITER );
		try
		{
			if( tokens.length == 2 )
			{
				Port.valueOf( tokens[ 0 ] );
				if( Integer.parseInt( tokens[ 1 ] ) >= 0 )
				{
					return tokens;
				}
			}
		}
		catch( IllegalArgumentException e )
		{
			_logger.log( Level.WARNING, "", e );
		}
		return null;
	}

	/**
	 * true if updating the specified port with the specified sensitivity successful
	 *
	 * @param   port		the specified port
	 * @param   sensitivity the specified sensitivity
	 * @return  true if modifying the specified port with the specified sensitivity successful
	 *
	 * @author  snowfoxy
	 * @since   2011-11-22
	 */
	protected boolean updatePort( final String port, final String sensitivity )
	{
		try
		{
			String[] tokens = splitPort( port );
			int index = Integer.parseInt( tokens[ 1 ] );
			switch( Port.valueOf( tokens[ 0 ] ) )
			{
				case A:
					_handler.setSensorChangeTrigger( index, Integer.parseInt( sensitivity ) );
					break;
				case DI: case DO:
					break;
			}
			return true;
		}
		catch( PhidgetException e )
		{
			_logger.log( Level.WARNING, "", e );
		}
		return false;
	}

	/**
	 * return the status of the write command with the specified port
	 *
	 * @param	port	the specified port
	 * @return	the status of the write command with the specified port
	 *
	 * @author	snowfoxy
	 * @since	2011-11-16
	 */
	protected Command.Message write( final String port )
	{
		try
		{
			String[] tokens = splitPort( port );
			int index = Integer.parseInt( tokens[ 1 ] );
			switch( Port.valueOf( tokens[ 0 ] ) )
			{
				case DO:
					_handler.setOutputState( index, true );
					long current = System.currentTimeMillis();
					while( System.currentTimeMillis() - current < 1000 )
					{
					}
					_handler.setOutputState( index, false );
					return Command.Message.OK;
			}
		}
		catch( PhidgetException e )
		{
			_logger.log( Level.WARNING, "", e );
			return Command.Message.EXCEPTION_PHIDGET;
		}
		return Command.Message.BAD_COMMAND;
	}
}
